/**
 *  \file I3Trigger.cpp
 *  \brief This file defines an i3 trigger.
 */

#include "OCD_include.h"

#include "i3_debug.h"
#include "I3Trigger.h"
#include "I3Functor.h"
#include "I3Id.h"
#include "I3OCDStateInfo.h"
#include "I3ClientContext.h"
#include "I3Params.h"
#include "I3Packet.h"
#include "I3TimerEvent.h"


extern "C" {
    #include "i3.h"
    #include "i3_id.h"
    #include "../utils/gen_utils.h"//TODO change this
}

#include "i3CommonHeaders.h"
#include "I3OverlayNetwork.h"

/** 
  * Simple constructor.
  */
I3Trigger::I3Trigger(I3OverlayNetwork* overlay) {
    this->maxBW = -1;
    this->maxConn = -1;


    killMeFlag = false;
    i3Id = NULL;
    i3Key = NULL;
    cl_trig = NULL;
    ocdState = NULL;
    trigInsertTimer = NULL;
    insertAttemptsCount = 0;
    
    timer = NULL;

    i3Overlay = overlay;

    isAnycastFlag = false;
}

/**
  * Creates a new I3Trigger with the specified i3 id.
  */
I3Trigger::I3Trigger(ID *id, I3OverlayNetwork* overlay) {

    this->maxBW = -1;
    this->maxConn = -1;

    killMeFlag = false;
    i3Id = NULL;
    i3Key = NULL;
    cl_trig = NULL;
    ocdState = NULL;
    trigInsertTimer = NULL;
    insertAttemptsCount = 0;

    timer = NULL;
    this->setI3Id(id);

    i3Overlay = overlay;
    isAnycastFlag = false;
}

/**
  * Constructs a wrapper I3Trigger object over the given cl_trigger.
  * @param trig The cl_trigger being wrapped around.
  * @param optType The option type of this trigger when it was received.
  */
I3Trigger::I3Trigger(cl_trigger* cl_trig, int optType, I3OverlayNetwork* overlay) {
    
    this->maxBW = -1;
    this->maxConn = -1;

    killMeFlag = false;
    i3Id = NULL;
    i3Key = NULL;
    ocdState = NULL;
    trigInsertTimer = NULL;
    insertAttemptsCount = 0;
    
    this->cl_trig = cl_trig;
    this->optType = optType;
    
    timer = NULL;
    i3Overlay = overlay;
    isAnycastFlag = false;
}

/**
  * This function sets the functor (function) to be called when an event
  * associated with a trigger happens.
  * @param fn The callback functor.
  * @param callbackType the type of event which invokes this callback.
  */
void I3Trigger::registerCallback(I3Functor *fn, int callbackType) {
    
    callbackMap.insert(pair<int, I3Functor *>(callbackType, fn));
}

/**
  * This function executes the callbacks registered on the trigger.
  * @param eventType The event type which determines which callback is to be executed.
  * @param data Some data associatd with the callback.  May be null.  Its use depends on the specific callback which is executed.
  */

void I3Trigger::doCallbacks(int eventType, cbk_packet *pkt) {
   
    I3Functor *callbackFunctor = lookupCallback(eventType);
    if (callbackFunctor != NULL) {
        (*callbackFunctor)(this, pkt);
    } else {
        I3_PRINT_DEBUG0(I3_DEBUG_LEVEL_MINIMAL, "Callback on null I3Trigger functor.\n");
    }
}

/**
  * Look up the callback associated with a particular type or event.
  * @param callbackType the type of callback used when registering the callback.
  * @return the corresponding I3Functor or NULL if a callback of the requested type is not registered with this trigger.
  */
I3Functor* I3Trigger::lookupCallback(int callbackType) {
   
    map<int, I3Functor*>::iterator itr = callbackMap.find(callbackType);
   
    if (itr != callbackMap.end()) {
        return (I3Functor*)itr->second;
        
    } else {
            
        return NULL;
    }   
}

/**
  * Clears all callbacks associated with this trigger.
  * Please note that it does not remove the callbacks inside the i3 API.
  * TODO:  Don't those callbacks get automatically removed when the trigger times out?
  */
void I3Trigger::clearCallbacks () {

    callbackMap.clear();
}


/** Sets the I3Id of this trigger.
 *  @param tmpId the value of the i3 id.
 */
void I3Trigger::setI3Id(ID *tmpId) {
    
    if (this->i3Id != NULL) {
        delete i3Id;
    }
    this->i3Id = new I3Id(tmpId);

    char tmpBuf[10000];
    I3_PRINT_DEBUG2(I3_DEBUG_LEVEL_VERBOSE, 
		"\n\nAllocate Trigger->i3Id = %p for id %s\n",
		this->i3Id, sprintf_i3_id (tmpBuf, tmpId));
}

/**
  * Create a new id of the trigger.
  * The id is currently blank and will be set later.
  */
void I3Trigger::createNewBlankId() {
 
    if (this->i3Id != NULL) {
        delete i3Id;
    }
    this->i3Id = new I3Id();

}

/**
  * This function returns true if this is a public i3 trigger.  Else returns false.
  */
bool I3Trigger::isPublic() {
    if (triggerType == "public") {
        return true;
    } else {
        return false;
    }
}

/**
 *  Returns true if this is an anycast trigger.
 */
bool I3Trigger::isAnycast() {
    return isAnycastFlag;
}

/**
  * This function returns true if this is a private i3 trigger.  Else returns false.
  */
bool I3Trigger::isPrivate() {

    return i3Id->isPrivate();
}


/**
  * This function just returns a random time interval between
  * I3_MIN_TRIGGER_REFRESH_PERIOD and I3_MAX_TRIGGER_REFRESH_PERIOD.
  */
uint64_t I3Trigger::getRandomTriggerRefreshInterval() {
    return (uint64_t) (funif_rand(I3_MIN_TRIGGER_REFRESH_PERIOD, I3_MAX_TRIGGER_REFRESH_PERIOD));

}

/** Destructor */
I3Trigger::~I3Trigger () {

    if (timer != NULL) {
        timer->setDisabled();
        timer = NULL;
    }

    if (trigInsertTimer != NULL) {
        trigInsertTimer->setDisabled();
        trigInsertTimer = NULL;
    }

    if (i3Id != NULL) {
        delete i3Id;
    }

    //TODO delete i3Kye and i3Id??

    clearCallbacks();

    if (cl_trig != NULL) {
       i3Overlay->context->destroyTrigger(cl_trig); 
    }
}


void I3Trigger::setRateLimit (int maxConn, int maxBW) {
    this->maxConn = maxConn;
    this->maxBW = maxBW;
}
